// Seir programming Editor
// Copyright (c) <2007> <David Torres>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Net;
using Gtk;
using GtkSharp;

namespace Seir
{
    public class sMain : Gtk.Window
    {
        #region Variables

        public string WindowTitle = "Seir Text Editor";
        private VBox s_MainFrmPanel;
        private VBox s_MenuAndToolbarPanel;
        private MenuBar s_MainMenuBar;
        private Toolbar s_MainToolbar;
        private Statusbar s_StatusBar;
        private AccelGroup s_AccelGroup;
        private sView s_EditView;
        private HTML html;
        private sDocument s_Document;
        private int s_nDocNum = 1;
        private Gtk.Label s_CursorPosition;

        #endregion

        #region Constructor
        // sMain initializes components, displays all child views, and inherits from
        // <code>base</code> to access members of the base class from inside the derived class
        public sMain()
            : base("Seir Text Editor")
        {
            initComponents();
            this.ShowAll();
            this.CreateNewDocument();
        }

        #endregion

        #region Main Edit View

        public sView EditView
        {
            get
            {
                return s_EditView;
            }
        }

        #endregion

        #region Initialization

        protected void initComponents()
        {
            // Create a new instance of the global hAccelerator group
            s_AccelGroup = new AccelGroup();

            // sMain
            // Set the Windows characteristics
            this.Title = this.WindowTitle;
            this.SetDefaultSize(400, 300);
            this.AddAccelGroup(s_AccelGroup);
            this.DeleteEvent += new DeleteEventHandler(OnWindowDelete);

            // MainFrmPanel
            s_MainFrmPanel = new VBox(false, 0);

            // s_MenuAndToolbarPanel
            s_MenuAndToolbarPanel = new VBox(false, 0);

            #region Menu bar

            s_MainMenuBar = new Gtk.MenuBar();

            // File menu
            Menu FileMenu = new Menu();
            MenuItem FileMenuItem = new MenuItem("_File");
            FileMenuItem.Submenu = FileMenu;
            FileMenu.AccelGroup = s_AccelGroup;

            // Edit menu
            Menu EditMenu = new Menu();
            MenuItem EditMenuItem = new MenuItem("_Edit");
            EditMenuItem.Submenu = EditMenu;
            EditMenu.AccelGroup = s_AccelGroup;

            // View menu
            Menu ViewMenu = new Menu();
            MenuItem ViewMenuItem = new MenuItem("_View");
            ViewMenuItem.Submenu = ViewMenu;
            ViewMenu.AccelGroup = s_AccelGroup;

            // Tools menu
            Menu ToolsMenu = new Menu();
            MenuItem ToolsMenuItem = new MenuItem("_Tools");
            ToolsMenuItem.Submenu = ToolsMenu;
            ToolsMenu.AccelGroup = s_AccelGroup;

            // Help menu
            Menu HelpMenu = new Menu();
            MenuItem HelpMenuItem = new MenuItem("_Help");
            HelpMenuItem.Submenu = HelpMenu;
            HelpMenu.AccelGroup = s_AccelGroup;

            // File New menu item
            Gtk.ImageMenuItem NewMenuItem = new ImageMenuItem("gtk-new", s_AccelGroup);
            NewMenuItem.Activated += new EventHandler(NewMenuItes_Activated);
            FileMenu.Append(NewMenuItem);

            // File Open menu item
            Gtk.ImageMenuItem OpenMenuItem = new ImageMenuItem("gtk-open", s_AccelGroup);
            OpenMenuItem.Activated += new EventHandler(OpenMenuItes_OnActivate);
            FileMenu.Append(OpenMenuItem);

            // File Save menu item
            Gtk.ImageMenuItem SaveMenuItem = new ImageMenuItem("gtk-save", s_AccelGroup);
            SaveMenuItem.Activated += new EventHandler(SaveMenuItes_Activated);
            FileMenu.Append(SaveMenuItem);

            // File SaveAs menu item
            Gtk.ImageMenuItem SaveAsMenuItem = new ImageMenuItem("gtk-save-as", s_AccelGroup);
            SaveAsMenuItem.Activated += new EventHandler(SaveAsMenuItes_Activated);
            FileMenu.Append(SaveAsMenuItem);

            // File Separator menu item
            SeparatorMenuItem Separator1MenuItem = new SeparatorMenuItem();
            FileMenu.Append(Separator1MenuItem);

            // File Exit menu item
            Gtk.ImageMenuItem ExitMenuItem = new ImageMenuItem("gtk-quit", s_AccelGroup);
            ExitMenuItem.Activated += new EventHandler(ExitMenuItes_OnActivate);
            FileMenu.Append(ExitMenuItem);

            // Edit Cut menu item
            Gtk.ImageMenuItem CutMenuItem = new ImageMenuItem("gtk-cut", s_AccelGroup);
            CutMenuItem.Activated += new EventHandler(CutMenuItes_Activated);
            EditMenu.Append(CutMenuItem);

            // Edit Copy menu item
            Gtk.ImageMenuItem CopyMenuItem = new ImageMenuItem("gtk-copy", s_AccelGroup);
            CopyMenuItem.Activated += new EventHandler(CopyMenuItes_Activated);
            EditMenu.Append(CopyMenuItem);

            // Edit Paste menu item
            Gtk.ImageMenuItem PasteMenuItem = new ImageMenuItem("gtk-paste", s_AccelGroup);
            PasteMenuItem.Activated += new EventHandler(PasteMenuItes_OnActivate);
            EditMenu.Append(PasteMenuItem);

            // Tools Show Available Languages menu item
            MenuItem ShowAvailableLanguagesMenuItem = new MenuItem("Show _Available Languages...");
            ShowAvailableLanguagesMenuItem.Activated += new EventHandler(ShowAvailableLanguagesMenuItes_Activated);
            ToolsMenu.Append(ShowAvailableLanguagesMenuItem);

            // Help About menu item
            MenuItem AboutMenuItem = new MenuItem("_About...");
            AboutMenuItem.Activated += new EventHandler(AboutMenuItes_Activated);
            HelpMenu.Append(AboutMenuItem);

            // Add the menus to the menubar		
            s_MainMenuBar.Append(FileMenuItem);
            s_MainMenuBar.Append(EditMenuItem);
            s_MainMenuBar.Append(ViewMenuItem);
            s_MainMenuBar.Append(ToolsMenuItem);
            s_MainMenuBar.Append(HelpMenuItem);

            // Add the menubar to the Menu and Toolbar panel
            s_MenuAndToolbarPanel.PackStart(s_MainMenuBar, false, false, 0);

            #endregion

            #region Toolbar

            s_MainToolbar = new Toolbar();

            // Toolbar buttons
            Gtk.ToolButton NewToolBarBtn = new Gtk.ToolButton("gtk-new");
            NewToolBarBtn.Clicked += new EventHandler(NewToolBarBtn_Clicked);

            Gtk.ToolButton OpenToolBarBtn = new Gtk.ToolButton("gtk-open");
            OpenToolBarBtn.Clicked += new EventHandler(OpenToolBarBtn_Clicked);

            Gtk.ToolButton SaveToolBarBtn = new Gtk.ToolButton("gtk-save");
            // SaveToolBarBtn.Clicked +=new EventHandler(SaveToolBarBtn_Clicked);
            SaveToolBarBtn.Clicked += new EventHandler(SaveToolBarBtn_Clicked);

            Gtk.ToolButton CutToolBarBtn = new Gtk.ToolButton("gtk-cut");
            CutToolBarBtn.Clicked += new EventHandler(CutToolBarBtn_Clicked);
            // CutToolBarBtn.Clicked +=new EventHandler(CutToolBarBtn_Clicked);

            Gtk.ToolButton CopyToolBarBtn = new Gtk.ToolButton("gtk-copy");
            CopyToolBarBtn.Clicked += new EventHandler(CopyToolBarBtn_Clicked);

            Gtk.ToolButton PasteToolBarBtn = new Gtk.ToolButton("gtk-paste");
            PasteToolBarBtn.Clicked += new EventHandler(PasteToolBarBtn_Clicked);

            // Add the buttons to the toolbar

            // File operations buttons
            s_MainToolbar.Add(NewToolBarBtn);
            s_MainToolbar.Add(OpenToolBarBtn);
            s_MainToolbar.Add(SaveToolBarBtn);

            // Insert Separator
            s_MainToolbar.Add(new Gtk.SeparatorToolItem());

            // Add the editing buttons
            s_MainToolbar.Add(CutToolBarBtn);
            s_MainToolbar.Add(CopyToolBarBtn);
            s_MainToolbar.Add(PasteToolBarBtn);

            // Add the toolbar to the Menu and Toolbar panel
            s_MenuAndToolbarPanel.PackStart(s_MainToolbar, false, false, 0);

            #endregion

            #region Edit View

            s_EditView = new sView();
            s_EditView.EditView.Buffer.Changed += new EventHandler(Buffer_Changed);
            s_EditView.EditView.Buffer.InsertText += new InsertTextHandler(Buffer_InsertText);
            s_EditView.EditView.Buffer.MarkSet += new MarkSetHandler(Buffer_MarkSet);
            s_EditView.EditView.MoveCursor += new MoveCursorHandler(EditView_MoveCursor);


            #endregion

            #region Status bar

            s_CursorPosition = new Label("Ln 0 Col 0");

            s_StatusBar = new Statusbar();
            s_StatusBar.PackEnd(s_CursorPosition, false, true, 30);

            #endregion

            // Add the MenuAndToolBar panel to the Main Form panel
            s_MainFrmPanel.PackStart(s_MenuAndToolbarPanel, false, false, 0);

            // Add the EditView Main Form panel
            s_MainFrmPanel.PackStart(s_EditView, true, true, 2);

            // Add the Status bar to the Main Window
            s_MainFrmPanel.PackStart(s_StatusBar, false, true, 1);

            // Add Panels to the Main Window
            this.Add(s_MainFrmPanel);
        }

        #endregion

        #region Buffer Actions

        private void Buffer_InsertText(object o, InsertTextArgs args)
        {
            UpdateCursorPositionIndicator();
        }

        private void Buffer_Changed(object sender, EventArgs e)
        {
            if (this.s_Document != null)
            {
                this.s_Document.IsModified = true;
                SetMainFrmTitle();
            }

            UpdateCursorPositionIndicator();
        }

        private void EditView_MoveCursor(object o, MoveCursorArgs args)
        {
            UpdateCursorPositionIndicator();
        }

        private void Buffer_MarkSet(object o, MarkSetArgs args)
        {
            this.UpdateCursorPositionIndicator();
        }

        public void OnWindowDelete(object o, DeleteEventArgs args)
        {
            CheckIfModifiedAndSave();

            Application.Quit();
            args.RetVal = true;
        }

        #endregion

        #region Menu event handler methods

        private void NewMenuItes_Activated(object sender, EventArgs e)
        {
            CreateNewDocument();
        }

        private void OpenMenuItes_OnActivate(object o, EventArgs args)
        {
            OpenDocument();
        }

        private void SaveMenuItes_Activated(object sender, EventArgs e)
        {
            if (this.s_Document.IsNewDocument == false)
            {
                SaveTextBufferContentToFile(this.s_Document.FileName);
                this.s_Document.IsNewDocument = false;
                this.s_Document.IsModified = false;
                SetMainFrmTitle();
            }
            else
            {
                this.SaveAs();
            }
        }

        private void SaveAsMenuItes_Activated(object sender, EventArgs e)
        {
            this.SaveAs();
        }

        private void ExitMenuItes_OnActivate(object o, EventArgs args)
        {
            CheckIfModifiedAndSave();

            Application.Quit();
        }

        private void CutMenuItes_Activated(object sender, EventArgs e)
        {
            this.CutToClipboard();
        }

        private void CopyMenuItes_Activated(object sender, EventArgs e)
        {
            this.CopyToClipboard();
        }

        private void PasteMenuItes_OnActivate(object sender, EventArgs e)
        {
            PasteFromClipboard();
        }

        private void ShowAvailableLanguagesMenuItes_Activated(object sender, EventArgs e)
        {
            // Send a list of the available Languages to stdout
            GtkSourceView.SourceLanguage[] theArray = null;
            theArray = this.s_EditView.GetAllAvailableLanguages();

            Console.Write("List of available Languages:\n");
            foreach (GtkSourceView.SourceLanguage aLang in theArray)
            {
                System.Console.Write("{0}\n", aLang.Name);
            }

        }

        private void AboutMenuItes_Activated(object sender, EventArgs e)
        {
            sAbout AboutDlg = new sAbout(this);
            AboutDlg.Authors = new string[] { "David W Torres" };
            int nRc = AboutDlg.Run();

            AboutDlg.Hide();

            if (nRc == (int)ResponseType.Ok)
            {
            }
        }

        #endregion

        #region Toolbar buttons event handler methods

        private void NewToolBarBtn_Clicked(object sender, EventArgs e)
        {
            CreateNewDocument();
        }

        // File Open clicked event
        private void OpenToolBarBtn_Clicked(object sender, EventArgs e)
        {
            OpenDocument();
        }

        private void SaveToolBarBtn_Clicked(object sender, EventArgs e)
        {
            if (this.s_Document.IsNewDocument == false)
            {
                SaveTextBufferContentToFile(this.s_Document.FileName);
                this.s_Document.IsNewDocument = false;
                this.s_Document.IsModified = false;
                SetMainFrmTitle();
            }
            else
            {
                this.SaveAs();
            }
        }


        private void CutToolBarBtn_Clicked(object sender, EventArgs e)
        {
            this.CutToClipboard();
        }

        private void CopyToolBarBtn_Clicked(object sender, EventArgs e)
        {
            this.CopyToClipboard();
        }

        private void PasteToolBarBtn_Clicked(object sender, EventArgs e)
        {
            this.PasteFromClipboard();
        }

        #endregion

        #region Common methods

        // New Document
        protected void CreateNewDocument()
        {
            if (this.s_Document != null)
            {
                if (this.s_Document.IsModified == true)
                {
                    // Offer to save
                    // Prompt to save changes
                    // Use Yes No Cancel
                    MessageDialog md = new MessageDialog(
                        this,
                        DialogFlags.DestroyWithParent,
                        MessageType.Info,
                        ButtonsType.YesNo,
                        String.Format("{0} has been modified.\nDo you wish to save?",
                        System.IO.Path.GetFileName(this.s_Document.FileName)
                        )
                        );

                    md.Modal = true;
                    Gtk.ResponseType result = (Gtk.ResponseType)md.Run();
                    md.Hide();
                    if (result == Gtk.ResponseType.Yes)
                    {
                        // Save away
                        this.SaveTextBufferContentToFile(this.s_Document.FileName);
                    }
                }
            }

            LoadNewDocumentAndClearBuffer();
            return;
        }
        
        // Does the Open Document Logic
        protected void OpenDocument()
        {
            string strFileName = this.SelectFile();

            // Simple check to bail or not
            if (strFileName.Length < 1)
            {
                return;
            }

            if (this.s_Document == null)
            {
                this.s_Document = new sDocument();
                LoadDocumentAndBuffer(strFileName);
                return;
            }
            else
            {
                this.CheckIfModifiedAndSave();
                LoadDocumentAndBuffer(strFileName);
                return;
            }
        }

        // Set the Main Window Title bar
        protected void SetMainFrmTitle()
        {
            this.Title = String.Format("{0} - {1}{2}",
                WindowTitle,
                this.s_Document.FileName,
                (this.s_Document.IsModified == true) ? "*" : ""
                );
        }

        // Selects a file using the FileSelection dialog for
        // the Save As menu functionality
        protected string SaveAsSelectFile(string strFileName)
        {
            // Create and populate button array
            // for the FileChooserDialog
            object[] button_data = new object[4];
            button_data[0] = Stock.Cancel;
            button_data[1] = ResponseType.Cancel;
            button_data[2] = Stock.Save;
            button_data[3] = ResponseType.Ok;

            // Notice that the Action "Save" is what
            // will guide the behavior and appearance of 
            // the dialog for "Save As" functionality
            FileChooserDialog fDlg = new FileChooserDialog(
                    "Save", this,  FileChooserAction.Save, button_data );

            // Set to modal
            fDlg.Modal = true;

            // Pre-poulate the file name entry box
            // with the name the file already had
            fDlg.SetFilename(strFileName);

            int nRc = fDlg.Run();
            fDlg.Hide();

            if (nRc == (int)ResponseType.Ok)
            {
                return fDlg.Filename;
            }

            return "";
        }

        // Selects a file using the FileChooserDialog 
        protected string SelectFile()
        {
            // Create and populate button array
            // for the FileChooserDialog
            object[] button_data = new object[4];
            button_data[0] = Stock.Cancel;
            button_data[1] = ResponseType.Cancel;
            button_data[2] = Stock.Open;
            button_data[3] = ResponseType.Ok;

            FileChooserDialog fDlg = new FileChooserDialog(
                    "Choose a File", this, FileChooserAction.Open,button_data);

            fDlg.Modal = true;

            int nRc = fDlg.Run();
            fDlg.Hide();

            if (nRc == (int)ResponseType.Ok)
            {
                return fDlg.Filename;
            }

            return "";
        }

        // Save As Logic
        protected void SaveAs()
        {
            string strFileName = SaveAsSelectFile(this.s_Document.FileName);
            if (strFileName.Length > 0)
            {
                SaveTextBufferContentToFile(strFileName);
                this.s_Document.FileName = strFileName;
                this.s_Document.IsNewDocument = false;
                this.s_Document.IsModified = false;
                SetMainFrmTitle();
            }
        }

        // Saves the contents of the TextBuffer to the
        // specified file.
        protected void SaveTextBufferContentToFile(string strFileName)
        {
            try
            {
                this.s_Document.SaveDocument(
                    strFileName,
                    this.EditView.EditView.Buffer.Text
                    );
                this.s_Document.IsModified = false;
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc.Message);
            }

            return;
        }

        protected void CheckIfModifiedAndSave()
        {
            if (this.s_Document.IsModified == true)
            {
                // Offer to save
                // Prompt to save changes
                // Use Yes No Cancel
                MessageDialog md = new MessageDialog(
                    this,
                    DialogFlags.DestroyWithParent,
                    MessageType.Info,
                    ButtonsType.YesNo,
                    String.Format("{0} has been modified.\nDo you wish to save?",
                    System.IO.Path.GetFileName(this.s_Document.FileName)
                    )
                    );

                md.Modal = true;
                Gtk.ResponseType result = (Gtk.ResponseType)md.Run();
                md.Hide();
                if (result == Gtk.ResponseType.Yes)
                {
                    // Save away
                    this.SaveTextBufferContentToFile(this.s_Document.FileName);
                }
            }
        }

        // Loads a new document and clears the buffer
        protected void LoadNewDocumentAndClearBuffer()
        {
            GtkSourceView.SourceLanguagesManager slm = new GtkSourceView.SourceLanguagesManager();
            GtkSourceView.SourceLanguage sl = slm.GetLanguageFromMimeType("text/x-c");

            if (sl == null)
            {
                throw new Exception("Problems with GtkSourceView's SourceLanguage creation");
            }

            this.s_EditView.SourceBuffer.Language = sl;
            this.s_EditView.SourceBuffer.Highlight = false;

            this.s_EditView.EditView.Buffer.Clear();
            this.s_Document = new sDocument();
            this.s_Document.FileName = String.Format(
                "document{0}.txt",
                this.s_nDocNum++
                );
            this.s_Document.IsNewDocument = true;
            SetMainFrmTitle();
        }

        // Opens the specified file as a UTF-8 text file
        // and loads it in the TextView's Buffer
        protected void LoadDocumentAndBuffer(string strFileName)
        {
            // Select the appropriate source highlighting base
            // on the file extension
            this.s_EditView.SourceBuffer.Language = GetSourceLanguageByFileName(strFileName);
            this.s_EditView.SourceBuffer.Highlight = true;

            this.s_Document.OpenDocumentForRead(strFileName);
            this.EditView.EditView.Buffer.Text = this.s_Document.TextStream.ReadToEnd();
            this.s_Document.IsNewDocument = false;
            this.s_Document.IsModified = false;

            this.s_Document.TextStream.Close();

            SetMainFrmTitle();
            return;
        }
        
        // Update the status bar display to reflect the cursor's position
        public void UpdateCursorPositionIndicator()
        {
            Gtk.TextIter curTextIter = this.s_EditView.EditView.Buffer.GetIterAtMark(this.s_EditView.EditView.Buffer.InsertMark);
            this.SetLineCol(curTextIter.Line + 1,
                curTextIter.LineOffset + 1
                );
        }

        protected void SetLineCol(int Line, int Column)
        {
            this.s_CursorPosition.Text = String.Format("Ln {0} Col {1}", Line, Column);
        }

        protected void CutToClipboard()
        {
            Gtk.TextBuffer buff = this.s_EditView.EditView.Buffer;
            Gtk.Clipboard clp = Gtk.Clipboard.Get(Gdk.Atom.Intern("CLIPBOARD", true));

            buff.CutClipboard(clp, true);
        }

        protected void CopyToClipboard()
        {
            Gtk.TextBuffer buff = this.s_EditView.EditView.Buffer;
            Gtk.Clipboard clp = Gtk.Clipboard.Get(Gdk.Atom.Intern("CLIPBOARD", true));

            buff.CopyClipboard(clp);
        }

        protected void PasteFromClipboard()
        {
            Gtk.TextBuffer buff = this.s_EditView.EditView.Buffer;
            Gtk.Clipboard clp = Gtk.Clipboard.Get(Gdk.Atom.Intern("CLIPBOARD", true));

            buff.PasteClipboard(clp);
        }

        protected GtkSourceView.SourceLanguage GetSourceLanguageByFileName(string fileName)
        {
            GtkSourceView.SourceLanguagesManager slm = new GtkSourceView.SourceLanguagesManager();
            GtkSourceView.SourceLanguage slRetVal = null;
            string fileExt = System.IO.Path.GetExtension(fileName);

            switch (fileExt)
            {
                case ".h":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-c");
                    break;
                case ".c":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-c");
                    break;
                case ".cpp":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-cpp");
                    break;
                case ".cs":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-csharp");
                    break;
                case ".xml":
                    slRetVal = slm.GetLanguageFromMimeType("text/xml");
                    break;
                case ".sql":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-sql");
                    break;
                case ".rb":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-ruby");
                    break;
                case ".py":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-python");
                    break;
                case ".for":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-fortran");
                    break;
                case ".java":
                    slRetVal = slm.GetLanguageFromMimeType("text/x-java");
                    break;
                default:
                    slRetVal = slm.GetLanguageFromMimeType("text/x-c");
                    break;
            }

            Console.WriteLine("Selected Languange:{0}.", slRetVal.Name);

            return slRetVal;
        }

        #endregion

    }
}
